package cn.snmp.mutual.uk.stack;

import java.util.*;


import cn.snmp.mutual.uk.event.RawPduListener;

import java.io.*;

public class ListeningContextPool implements ListeningContextFace {
    private static final String version_id = "@(#)$Id: ListeningContextPool.java,v 3.7 2009/03/05 13:27:41 birgita Exp $ Copyright Westhawk Ltd";
    protected static Hashtable contextPool;
    protected ListeningContext context;
    protected String socketType;
    protected String bindAddr;
    protected int hostPort;

    public ListeningContextPool(final int port) {
        this(port, null, "Standard");
    }

    public ListeningContextPool(final int port, final String bindAddress) {
        this(port, bindAddress, "Standard");
    }

    public ListeningContextPool(final int port, final String bindAddress, final String typeSocket) {
        this.context = null;
        initPools();
        this.hostPort = port;
        this.bindAddr = bindAddress;
        this.socketType = typeSocket;
        this.context = this.getMatchingContext();
    }

    private static synchronized void initPools() {
        if (ListeningContextPool.contextPool == null) {
            ListeningContextPool.contextPool = new Hashtable(5);
        }
    }

    public int getPort() {
        return this.hostPort;
    }

    public String getBindAddress() {
        return this.bindAddr;
    }

    public String getTypeSocket() {
        return this.socketType;
    }

    public int getMaxRecvSize() {
        int res = 1472;
        if (this.context != null) {
            res = this.context.getMaxRecvSize();
        }
        return res;
    }

    public void setMaxRecvSize(final int no) {
        if (this.context == null) {
            this.context = this.getMatchingContext();
        }
        this.context.setMaxRecvSize(no);
    }

    public void destroy() {
        synchronized (ListeningContextPool.contextPool) {
            if (this.context != null) {
                final String hashKey = this.context.getHashKey();
                int count = 0;
                final Item item = (Item) ListeningContextPool.contextPool.get(hashKey);
                if (item != null) {
                    count = item.getCounter();
                    --count;
                    item.setCounter(count);
                }
                if (count <= 0) {
                    ListeningContextPool.contextPool.remove(hashKey);
                    this.context.destroy();
                }
                this.context = null;
            }
        }
    }

    public void destroyPool() {
        Hashtable copyOfPool = null;
        synchronized (ListeningContextPool.contextPool) {
            synchronized (ListeningContextPool.contextPool) {
                copyOfPool = (Hashtable) ListeningContextPool.contextPool.clone();
            }
            ListeningContextPool.contextPool.clear();
        }
        this.context = null;
        final Enumeration keys = copyOfPool.keys();
        while (keys.hasMoreElements()) {
            final String key = (String) keys.nextElement();
            final Item item = (Item) copyOfPool.get(key);
            if (item != null) {
                final ListeningContext cntxt = item.getContext();
                cntxt.destroy();
            }
        }
        copyOfPool.clear();
    }

    public ListeningContext getMatchingContext() {
        Item item = null;
        ListeningContext newContext = null;
        final String hashKey = this.getHashKey();
        this.destroy();
        synchronized (ListeningContextPool.contextPool) {
            int count = 0;
            if (ListeningContextPool.contextPool.containsKey(hashKey)) {
                item = (Item) ListeningContextPool.contextPool.get(hashKey);
                newContext = item.getContext();
                count = item.getCounter();
            } else {
                newContext = new ListeningContext(this.hostPort, this.bindAddr, this.socketType);
                item = new Item(newContext);
                ListeningContextPool.contextPool.put(hashKey, item);
            }
            ++count;
            item.setCounter(count);
        }
        return newContext;
    }

    public void dumpContexts(final String title) {
        Hashtable copyOfPool = null;
        synchronized (ListeningContextPool.contextPool) {
            copyOfPool = (Hashtable) ListeningContextPool.contextPool.clone();
        }
        System.out.println(title + " " + copyOfPool.size());
        final Enumeration keys = copyOfPool.keys();
        int i = 0;
        while (keys.hasMoreElements()) {
            final String key = (String) keys.nextElement();
            final Item item = (Item) copyOfPool.get(key);
            if (item != null) {
                final int count = item.getCounter();
                final ListeningContext cntxt = item.getContext();
                System.out.println("\tcontext: " + key + ", count: " + count + ", index: " + i + ", " + cntxt.toString());
                if (cntxt == this.context) {
                    System.out.println("\t\tcurrent context");
                }
                ++i;
            }
        }
    }

    public String getHashKey() {
        final String str = this.hostPort + "_" + this.bindAddr + "_" + this.socketType;
        return str;
    }

    public void addRawPduListener(final RawPduListener l) throws IOException {
        if (this.context != null) {
            this.context.addRawPduListener(l);
        }
    }

    public void removeRawPduListener(final RawPduListener l) {
        if (this.context != null) {
            this.context.removeRawPduListener(l);
        }
    }

    public void removeRawPduListenerFromPool(final RawPduListener l) {
        Hashtable copyOfPool = null;
        if (ListeningContextPool.contextPool != null) {
            synchronized (ListeningContextPool.contextPool) {
                copyOfPool = (Hashtable) ListeningContextPool.contextPool.clone();
            }
            final Enumeration keys = copyOfPool.keys();
            while (keys.hasMoreElements()) {
                final String key = (String) keys.nextElement();
                final Item item = (Item) copyOfPool.get(key);
                if (item != null) {
                    final ListeningContext cntxt = item.getContext();
                    cntxt.removeRawPduListener(l);
                }
            }
        }
    }

    public void addUnhandledRawPduListener(final RawPduListener l) throws IOException {
        if (this.context != null) {
            this.context.addUnhandledRawPduListener(l);
        }
    }

    public void removeUnhandledRawPduListener(final RawPduListener l) {
        if (this.context != null) {
            this.context.removeUnhandledRawPduListener(l);
        }
    }

    @Override
    public String toString() {
        String res = "";
        if (this.context != null) {
            res = this.context.toString();
        }
        return res;
    }

    class Item {
        private ListeningContext context;
        private int counter;

        Item(final ListeningContext con) {
            this.context = null;
            this.counter = 0;
            this.context = con;
            this.counter = 0;
        }

        ListeningContext getContext() {
            return this.context;
        }

        int getCounter() {
            return this.counter;
        }

        void setCounter(final int i) {
            this.counter = i;
        }

        @Override
        public String toString() {
            final StringBuffer buffer = new StringBuffer("Item[");
            buffer.append("context=").append(this.context.toString());
            buffer.append(", counter=").append(this.counter);
            buffer.append("]");
            return buffer.toString();
        }
    }
}
